`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 输入图像数据流程控制
* - 将行缓冲中的数据推入图像处理流水线
*/

/* NOTE:
* 行控
* - 读取的像素数据是顺序的
* - 按照对开分区，依次写入
*
* 列控
* - 读取数据是按照数据组port顺序的
* - 按照折行分区，依次写入
*
* 行控
* - 写入sdram时，一次最多写入256像素
* - 每写完256像素，地址增加16'h0400，参考《SDRAM地址映射》
*
* 列控（1-8组数据)
* - 写入sdram时，一次最多写入256像素（正好32扫x8组)
* - 每写完256像素，一定会切换到下一个分区，所以地址不用递增（切换分区会重新计算地址）
* 
* 列控（9-16组数据)
* - 写入sdram时，一次最多写入256像素（正好16扫x16组)
* - 每写完256像素，地址增加16扫相应地址，参考《SDRAM地址映射》
*   （16扫模式下，会切换分区，不用递增；32扫模式下会增加16'h4000）
*
* 列控（17-24组数据)
* - 写入sdram时，一次最多写入192像素（正好8扫x24组)
* - 每写完192像素，地址增加8扫相应地址，参考《SDRAM地址映射》
*   (16扫模式下，16'h4000；32扫模式下，16'h2000）
*
* 列控（25-32组数据)
* - 写入sdram时，一次最多写入256像素（正好8扫x32组)
* - 每写完256像素，地址增加8扫相应地址，参考《SDRAM地址映射》
*   (16扫模式下，16'h4000；32扫模式下，16'h2000）
*/

module pixel_save_ctrl (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // frame sync
    input  wire         I_frame_start,
    // config
    input  wire         I_cfg_pixel_adj_en, // 逐点色度调整
    input  wire         I_cfg_gap_adj_en,   // 缝隙调整
    input  wire [3:0]   I_cfg_sector_count, // 分区数量
    input  wire [10:0]  I_cfg_sector_width, // 每个区域宽度（总是8的倍数)
    input  wire [1:0]   I_cfg_scan_mode,
    input  wire [1:0]   I_cfg_box_dir,      // 箱体方向
    input  wire [1:0]   I_cfg_col_loop,     // 列模式读取循环次数(数据组数/8,向上取整), 0表示4
    // pixel coe
    output wire         O_coe_load_req,  // 请求载入一行逐点调整系数
    output wire [9:0]   O_coe_load_row,  // 请求逐点调系数的行号
    input  wire         I_coe_load_busy, // 正在载入逐点调整系数
    // gap coe
    output wire         O_gap_coe_req,
    output wire [9:0]   O_gap_coe_row,
    input  wire         I_gap_coe_busy,
    // addr decoder
    output wire         O_decode_req,    // 行地址译码请求
    output wire [9:0]   O_decode_row,    // 行号
    output wire [2:0]   O_decode_sector, // 分区号
    input  wire         I_decode_done,   // 译码完成
    input  wire [20:0]  I_decode_addr,   // 译码结果
    // line buffer
    input  wire         I_row_start,   // 单周期脉冲，表明一行数据正在缓冲中，此时可以预读逐点调整系数
    input  wire [9:0]   I_row_num,     // 当前行号
    input  wire         I_row_ready,   // 电平有效，表明本行已缓冲完毕，可以读取数据
    output wire         O_row_ack,     // 当前行数据使用完毕
    output wire         O_pixel_req,   // 请求读取本行数据（顺序读取）
    input  wire [7:0]   I_pixel_data,  // 像素数据
    // write pixel
    output wire         O_write_start, // 开始写入
    input  wire         I_write_busy,  // 正在写入
    output wire [20:0]  O_write_addr,  // 写入SDRAM地址
    output wire [5:0]   O_write_len,   // 写入长度，32bitx16（8pixel）数量
    output wire [7:0]   O_write_data   // 写入数据
);
//------------------------Parameter----------------------
// fsm
localparam [3:0]
    IDLE  = 0,
    COE0  = 1,
    COE1  = 2,
    READY = 3,
    DEC0  = 4,
    DEC1  = 5,
    WPREP = 6,
    WREQ  = 7,
    WDATA = 8,
    WBUSY = 9,
    LOOP0 = 10,
    LOOP1 = 11,
    ACK   = 12;

// box direction
localparam [1:0]
    LANDSCAPE = 0, // 横向
    PORTRAIT0 = 1, // 纵向，第一个端口在左侧
    PORTRAIT1 = 2; // 纵向，第一个端口在右侧

//------------------------Local signal-------------------
// fsm
reg  [3:0]  state;
reg  [3:0]  next;
reg         write_over;
reg  [10:0] total_count; // 一个区域内的总像素数
reg  [2:0]  pixel_color;
reg  [8:0]  pixel_count; // 一次写的总像素数，小于256
reg  [8:0]  pixel_max;   // 一次写的最大像素数
reg         pixel_req;
reg  [3:0]  sector_id;
reg  [9:0]  row_num;

// write pixel
reg  [20:0] write_addr;
reg  [8:0]  write_len;

//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (I_frame_start)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (I_row_start)
                if (I_cfg_pixel_adj_en || I_cfg_gap_adj_en)
                    next = COE0;
                else
                    next = READY;
            else
                next = IDLE;
        end

        COE0: begin
            next = COE1;
        end

        COE1: begin
            if (!I_coe_load_busy && !I_gap_coe_busy)
                next = READY;
            else
                next = COE1;
        end

        READY: begin
            if (I_row_ready)
                next = DEC0;
            else
                next = READY;
        end

        DEC0: begin
            next = DEC1;
        end

        DEC1: begin
            if (I_decode_done)
                next = WPREP;
            else
                next = DEC1;
        end

        WPREP: begin
            next = WREQ;
        end

        WREQ: begin
            next = WDATA;
        end

        WDATA: begin
            if (write_over)
                next = WBUSY;
            else
                next = WDATA;
        end

        WBUSY: begin
            if (~I_write_busy)
                next = LOOP0;
            else
                next = WBUSY;
        end

        LOOP0: begin
            if (total_count > 1'b0)
                next = WPREP;
            else
                next = LOOP1;
        end

        LOOP1: begin
            if (sector_id == I_cfg_sector_count)
                next = ACK;
            else
                next = DEC0;
        end

        ACK: begin
            next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// write_over
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_over <= 1'b0;
    else if (pixel_req && pixel_color[1] && pixel_count == 1'b1)
        write_over <= 1'b1;
    else
        write_over <= 1'b0;
end

// total_count
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        total_count <= 1'b0;
    else if (state == DEC0)
        total_count <= I_cfg_sector_width;
    else if (state == WREQ)
        total_count <= total_count - pixel_count;
end

// pixel_color
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_color <= 3'b001;
    else if (state == WPREP)
        pixel_color <= 3'b001;
    else if (pixel_req)
        pixel_color <= {pixel_color[1:0], pixel_color[2]};
end

// pixel_count
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_count <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            pixel_count <= pixel_max;
        else
            pixel_count <= total_count[8:0];
    end
    else if (pixel_req && pixel_color[2])
        pixel_count <= pixel_count - 1'b1;
end

// pixel_max
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pixel_max <= 1'b0;
    else if (I_cfg_col_loop == 2'd3)
        pixel_max <= 9'd192;
    else
        pixel_max <= 9'd256;
end

// pixel_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_req <= 1'b0;
    else if (state == WPREP)
        pixel_req <= 1'b1;
    else if (write_over)
        pixel_req <= 1'b0;
end

// sector_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        sector_id <= 1'b0;
    else if (state == IDLE)
        sector_id <= 1'b0;
    else if (state == DEC1 && I_decode_done)
        sector_id <= sector_id + 1'b1;
end

// row_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        row_num <= 1'b0;
    else if (state == IDLE && I_row_start)
        row_num <= I_row_num;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++pixel coe++++++++++++++++++++++
assign O_coe_load_req = (state == COE0) && I_cfg_pixel_adj_en;
assign O_coe_load_row = row_num;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++gap coe++++++++++++++++++++++++
assign O_gap_coe_req = (state == COE0) && I_cfg_gap_adj_en;
assign O_gap_coe_row = row_num;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++addr decoder+++++++++++++++++++
assign O_decode_req    = (state == DEC0);
assign O_decode_row    = row_num;
assign O_decode_sector = sector_id[2:0];
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++line buffer++++++++++++++++++++
assign O_row_ack   = (state == ACK);
assign O_pixel_req = pixel_req;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++write pixel++++++++++++++++++++
assign O_write_start = (state == WREQ);
assign O_write_addr  = write_addr;
assign O_write_len   = write_len[8:3];
assign O_write_data  = I_pixel_data;

// write_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_addr <= 1'b0;
    else if (state == DEC1 && I_decode_done)
        write_addr <= I_decode_addr;
    else if (state == LOOP0) begin
        if (I_cfg_box_dir == LANDSCAPE)
        begin
            if(I_cfg_scan_mode==2)
                write_addr <= write_addr + 16'h0200;
            else
                write_addr <= write_addr + 16'h0400;
        end
        else if(I_cfg_scan_mode==1 && (I_cfg_col_loop==3 || I_cfg_col_loop==0))
            write_addr <= write_addr + 16'h2000;
        else
            write_addr <= write_addr + 16'h4000;
    end
end

// write_len
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_len <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            write_len <= pixel_max;
        else
            write_len <= total_count[7:0] + 3'd7; // 按8的倍数向上取整
    end
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++


endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
